package com.zhou.message.service.impl;

import com.zhou.util.NumberUtil;
import com.zhou.util.ObjectUtil;
import com.zhou.util.StringTool;
import com.zhou.framework.mail.MailMessage;
import com.zhou.framework.mail.MailReceiver;
import com.zhou.framework.mail.MailServerConfig;
import com.zhou.framework.mail.MailUtil;
import com.zhou.message.dao.*;
import com.zhou.message.dto.SendMessagePageDto;
import com.zhou.message.model.MessageSendModel;
import com.zhou.message.dto.SystemMessagePageDto;
import com.zhou.message.dto.SystemMessageQueryDto;
import com.zhou.message.mapper.MessageMapper;
import com.zhou.message.model.MessageReceiver;
import com.zhou.message.service.*;
import com.zhou.org.common.EnumConstant;
import com.zhou.org.dao.User;
import com.zhou.org.model.OrgTree;
import com.zhou.org.model.SendResult;
import com.zhou.org.service.OrgService;
import com.zhou.org.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MessageServiceImpl implements MessageService {
    @Autowired
    private AttachmentService attachmentService;
    @Autowired
    private OrgService orgService;
    @Autowired
    private UserService userService;
    @Autowired
    private MessageTemplateService messageTemplateService;
    @Autowired
    private MessageTemplateReceiverService templateReceiverService;
    @Autowired
    private SendMessageService sendMessageService;
    @Autowired
    private SendMessageDetailService sendMessageDetailService;
    @Autowired
    private SendMessageAttachService messageAttachService;
    @Autowired
    private ReceiveMessageService receiveMessageService;
    @Autowired
    private MessageMapper messageMapper;

    //查询接收的消息
    @Override
    public List<SystemMessagePageDto> getReceivePageList(SystemMessageQueryDto dto) {
        return messageMapper.getUserReceiveList(dto);
    }

    @Override
    public Map<String,Integer> getUserReceiveCount(Integer toUserId) {
        List<Map<String, Object>> list = messageMapper.getUserReceiveCount(toUserId);
        Map<String,Integer> r = new LinkedHashMap<>(list.size());
        for (Map<String, Object> l : list) {
            String status = MapUtils.getString(l,"STATUS","0");
            int cnt = MapUtils.getIntValue(l,"CNT",0);
            r.put(status,cnt);
        }
        return r;
    }

    /**
     * 查询发送的消息
     */
    @Override
    public List<SendMessagePageDto> getSendPageList(SystemMessageQueryDto dto) {
        return messageMapper.getSendMessageList(dto);
    }

    @Override
    public List<SystemMessagePageDto> getSendMessageList(SystemMessageQueryDto dto) {
        return messageMapper.getSystemMessageList(dto);
    }

    @Override
    public List<SendMessageDetail> getSendMessageDetailList(Map<String, Object> map) {
        return messageMapper.getSendMessageDetailList(map);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addMessage(SendMessage message) {
        List<MessageReceiver> receiver = message.getReceiver();
        int n = 0;
        if(receiver != null && receiver.size() > 0){
            //接收人
            Map<Integer,MessageReceiver> receiverMap = new HashMap<>(receiver.size());
            //抄送人
            Map<Integer,MessageReceiver> ccMap = new HashMap<>(receiver.size());
            List<Integer> receiveOrgIdList = new ArrayList<>(receiver.size());
            List<Integer> ccOrgIdList = new ArrayList<>(receiver.size());
            //orgId下级的所有用户
            Map<Integer,Set<Integer>> orgIdUserMapping = new HashMap<>(receiver.size());
            //存放合并的用户
            Set<Integer> receiveUserIdList = new LinkedHashSet<>(receiver.size());
            Set<Integer> ccUserIdList = new LinkedHashSet<>(receiver.size());
            for (MessageReceiver messageReceiver : receiver) {
                if(messageReceiver.getRecieveType().equals(EnumConstant.MSG_RECEIVE_TYPE_CC)){
                    ccMap.put(messageReceiver.getOrgId(),messageReceiver);
                    ccOrgIdList.add(messageReceiver.getOrgId());
                }else{
                    receiverMap.put(messageReceiver.getOrgId(),messageReceiver);
                    receiveOrgIdList.add(messageReceiver.getOrgId());
                }

            }
            //如果查询的orgid在之前查询的orgid下级，就不用再去查询了
            Set<Integer> orgTemp = new HashSet<>(5);
            for (Integer orgId : receiveOrgIdList) {
                MessageReceiver r = receiverMap.get(orgId);
                if (r.getOrgType().equals(EnumConstant.ORG_TYPE_USER)) {
                    receiveUserIdList.add(r.getUserId());
                } else {
                    if (!orgTemp.contains(orgId)) {
                        List<OrgTree> orgTree = orgService.getChildOrgObject(orgId);
                        Set<Integer> l = new HashSet<>(2);
                        /*Set<Integer> orgTemp2 = new HashSet<>(2);*/
                        this.readUser(orgTree, l, orgTemp);
                        orgIdUserMapping.put(orgId, l);
                        /*xiaMapping.put(orgId,orgTemp2);
                        orgTemp.addAll(orgTemp2);*/
                        receiveUserIdList.addAll(new HashSet<>(l));
                    }
                }

            }
            orgTemp = new HashSet<>(5);
            for (Integer orgId : ccOrgIdList) {
                MessageReceiver r = ccMap.get(orgId);
                if (r.getOrgType().equals(EnumConstant.ORG_TYPE_USER)) {
                    ccUserIdList.add(r.getUserId());
                } else {
                    if (orgIdUserMapping.containsKey(orgId)) {
                        ccUserIdList.addAll(orgIdUserMapping.get(orgId));
                    } else {
                        if (!orgTemp.contains(orgId)) {
                            List<OrgTree> orgTree = orgService.getChildOrgObject(orgId);
                            Set<Integer> l = new HashSet<>(2);
                            this.readUser(orgTree, l, orgTemp);
                            ccUserIdList.addAll(l);
                        }
                    }
                }


            }
            if(receiveUserIdList.size() == 0 && ccUserIdList.size() == 0){
                return -1;
            }
            List<Integer> allUserId = new ArrayList<>();
            allUserId.addAll(receiveUserIdList);
            allUserId.addAll(ccUserIdList);
            //int operator = MapUtils.getIntValue(map,"RECEIVE_TYPE",EnumConstant.MSG_RECEIVE_TYPE_NORMAL);
            List<User> userList = userService.getBaseMapper().selectBatchIds(allUserId);
            n = userList.size();
            if(userList.size() == 0){
                return -1;
            }
            Map<Integer,User> userMap = new HashMap<>(userList.size());
            for (User user : userList) {
                userMap.put(user.getUserId(),user);
            }
            /*if(true){
                return 111;
            }*/
            Date d = new Date();
            message.setCreateDt(d);
            message.setStatus(EnumConstant.MSG_SEND_STATUS_WAIT);
            sendMessageService.save(message);
            //保存附件
            List<Attachment> attachmentList = message.getAttachmentList();
            if(attachmentList != null && attachmentList.size() > 0){
                for (Attachment attach : attachmentList) {
                    attach.setType(EnumConstant.ATTACH_TYPE_MESSAGE);
                    attach.setCreateDt(new Date());
                    attach.setUserId(message.getFromUserId());
                    attachmentService.addAttach(attach);
                    messageAttachService.addMessageAttach(message.getId(),attach.getId());
                }
            }
            int type = message.getType();
            for (Integer uid : receiveUserIdList) {
                User user = userMap.get(uid);
                SendMessageDetail detail = new SendMessageDetail();
                if(type == EnumConstant.MSG_TYPE_NORMAL){
                    detail.setAddress(user.getUserId()+"");
                }else if(type == EnumConstant.MSG_TYPE_EMAIL){
                    detail.setAddress(user.getEmail());
                }
                detail.setReceiveUserId(user.getUserId());
                detail.setReceiveType(EnumConstant.MSG_RECEIVE_TYPE_NORMAL);
                //detail.setReceiveType(operator);
                detail.setSendId(message.getId());
                detail.setSendDt(d);
                detail.setStatus(EnumConstant.MSG_DETAIL_SEND_STATUS_WAIT);
                sendMessageDetailService.save(detail);
            }
            for (Integer uid : ccUserIdList) {
                User user = userMap.get(uid);
                SendMessageDetail detail = new SendMessageDetail();
                if(type == EnumConstant.MSG_TYPE_NORMAL){
                    detail.setAddress(user.getUserId()+"");
                }else if(type == EnumConstant.MSG_TYPE_EMAIL){
                    detail.setAddress(user.getEmail());
                }
                detail.setReceiveUserId(user.getUserId());
                detail.setReceiveType(EnumConstant.MSG_RECEIVE_TYPE_CC);
                //detail.setReceiveType(operator);
                detail.setSendId(message.getId());
                detail.setSendDt(d);
                detail.setStatus(EnumConstant.MSG_DETAIL_SEND_STATUS_WAIT);
                sendMessageDetailService.save(detail);
            }

        }
        return n;
    }
    private SendMessage fromMap(SystemMessagePageDto detail){
        int type = NumberUtil.safeToInteger(detail.getType(),EnumConstant.MSG_TYPE_NORMAL);
        Long sendId = NumberUtil.safeToLong(detail.getSendId(),0L);
        int fromUserId = NumberUtil.safeToInteger(detail.getFromUserId(),0);
        int timer = NumberUtil.safeToInteger(detail.getTimer(),0);
        int status = NumberUtil.safeToInteger(detail.getStatus(),0);
        int level = NumberUtil.safeToInteger(detail.getLevel(),0);
        String title = StringTool.safeToString(detail.getTitle(),"");
        String content = StringTool.safeToString(detail.getContent(),"");

        Date createDt = detail.getCreateDt();
        Date sendDt = detail.getSendDt();
        SendMessage message = new SendMessage();
        message.setId(sendId);
        message.setFromUserId(fromUserId);
        message.setTimer(timer);
        message.setStatus(status);
        message.setLevel(level);
        message.setTitle(title);
        message.setContent(content);
        message.setType(type);
        message.setCreateDt(createDt);
        message.setSendDt(sendDt);
        return message;
    }
    @Override
    public SendResult sendSynMessage(MessageSendModel dto) {
        int detailId1 = NumberUtil.safeToInteger(dto.getDetailId(),0);
        int sendId1 = NumberUtil.safeToInteger(dto.getSendId(),0);
        SendResult result = new SendResult();

        SystemMessageQueryDto queryDto = new SystemMessageQueryDto();
        BeanUtils.copyProperties(dto,queryDto);

        List<SystemMessagePageDto> messageDetailList = messageMapper.getSystemMessageList(queryDto);
        Set<Long> successSendDetail = new HashSet<>(10);
        Set<Long> failedSendDetail = new HashSet<>(10);
        Map<Long,SendMessage> sendList = new HashMap<>(10);
        Map<Long, MailMessage> mailMessageMap = new HashMap<>(5);
        Map<Long, List<Long>> sendDetailMapping = new HashMap<>(5);
        MailServerConfig config = MailServerConfig.getLocalMailConfig();
        List<ReceiveMessage> receiveMessageList = new ArrayList<>(10);

        Set<Long> sendIds = messageDetailList.stream().map(SystemMessagePageDto::getSendId).collect(Collectors.toCollection(LinkedHashSet::new));
        Map<Long, List<Integer>> messageAttachIds = messageAttachService.getMessageAttachIds(sendIds);


        for (SystemMessagePageDto detail : messageDetailList) {
            int type = NumberUtil.safeToInteger(detail.getType(),EnumConstant.MSG_TYPE_NORMAL);
            Long sendId = NumberUtil.safeToLong(detail.getSendId(),0L);
            Long detailId = NumberUtil.safeToLong(detail.getDetailId(),0L);
            int receiveUserId = NumberUtil.safeToInteger(detail.getReceiveUserId(),0);
            int detailStatus = NumberUtil.safeToInteger(detail.getDetailStatus(),EnumConstant.MSG_DETAIL_SEND_STATUS_WAIT);
            int receiveType = NumberUtil.safeToInteger(detail.getReceiveType(),EnumConstant.MSG_RECEIVE_TYPE_NORMAL);
            String title = StringTool.safeToString(detail.getTitle(),"");
            String content = StringTool.safeToString(detail.getContent(),"");
            String address = StringTool.safeToString(detail.getAddress(),"");

            if(detailId1 > 0 || detailStatus != EnumConstant.MSG_DETAIL_SEND_STATUS_SUC){
                List<Long> detailM = sendDetailMapping.computeIfAbsent(sendId,o->new ArrayList<>());
                if(!detailM.contains(detailId)){
                    detailM.add(detailId);
                }
                ReceiveMessage receive = new ReceiveMessage();
                receive.setReceiveDt(new Date());
                receive.setDeleteFlag(0);
                receive.setStatus(EnumConstant.MSG_RECEIVE_SEND_STATUS_WAIT);
                receive.setReceiveType(receiveType);
                receive.setSendDetailId(detailId);
                receive.setReceiveUserId(receiveUserId);
                //receive.setId(detailId);
                if(!sendList.containsKey(sendId)){
                    SendMessage smg = this.fromMap(detail);
                    smg.setStatus(EnumConstant.MSG_SEND_STATUS_SUC);
                    sendList.put(sendId,smg);
                }
                try{
                    receiveMessageList.add(receive);
                    if(type == EnumConstant.MSG_TYPE_EMAIL){
                        MailMessage mailMessage = mailMessageMap.get(sendId);
                        if(mailMessage == null){
                            mailMessage = new MailMessage(title,content);
                            List<Integer> idList = messageAttachIds.get(sendId);
                            if(ObjectUtil.isNotEmpty(idList)){
                                mailMessage.setAttachmentList(attachmentService.getAttachs(idList));
                            }
                            mailMessageMap.put(sendId,mailMessage);
                        }
                        MailReceiver mr = new MailReceiver(address,detailId);
                        if(StringTool.isEmpty(address)){
                            failedSendDetail.add(detailId);
                            sendList.get(sendId).setStatus(EnumConstant.MSG_SEND_STATUS_FAIL);
                        }else{
                            if(receiveType == EnumConstant.MSG_RECEIVE_TYPE_NORMAL){
                                mailMessage.addReceiver(mr);
                            }else if(receiveType == EnumConstant.MSG_RECEIVE_TYPE_CC){
                                mailMessage.addDuplicate(mr);
                            }
                        }

                    }else if(type == EnumConstant.MSG_TYPE_NORMAL){
                        successSendDetail.add(detailId);
                    }


                }catch (Exception e){
                    log.error("发送消息失败！",e);
                    sendList.get(sendId).setStatus(EnumConstant.MSG_SEND_STATUS_FAIL);
                    failedSendDetail.add(detailId);
                }
            }


        }
        if(mailMessageMap.size() > 0){
            for(Long sendId : mailMessageMap.keySet()){
                List<Long> dm = sendDetailMapping.get(sendId);
                try {
                    /*if(true){
                        throw new RuntimeException();
                    }*/
                    MailUtil.send(config,mailMessageMap.get(sendId));
                    if(dm != null){
                        successSendDetail.addAll(dm);
                    }

                } catch (Exception e) {
                    log.error("发送邮件失败！",e);
                    failedSendDetail.addAll(dm);
                    sendList.get(sendId).setStatus(EnumConstant.MSG_SEND_STATUS_FAIL);
                }
            }
        }
        if(sendList.size() > 0){
            for(Map.Entry<Long,SendMessage> entry : sendList.entrySet()){
                SendMessage sendMessage = entry.getValue();
                sendMessage.setSendDt(new Date());
                int finalStatus = sendMessage.getStatus();
                if(finalStatus == EnumConstant.MSG_SEND_STATUS_SUC && detailId1 > 0 && sendId1 == sendMessage.getId()){
                    //如果是通过detailId发送，需要更新SendMessage状态
                    SystemMessageQueryDto qd = new SystemMessageQueryDto();
                    qd.setSendId(dto.getSendId());
                    //查询出这个消息发送的所有detail
                    List<SystemMessagePageDto> sl = messageMapper.getSystemMessageList(qd);
                    int c = 0;
                    for (SystemMessagePageDto de : sl) {
                        int detailStatus = NumberUtil.safeToInteger(de.getDetailStatus(),EnumConstant.MSG_DETAIL_SEND_STATUS_WAIT);
                        if(detailStatus == EnumConstant.MSG_DETAIL_SEND_STATUS_WAIT){
                            c ++;
                        }
                        if(detailStatus == EnumConstant.MSG_DETAIL_SEND_STATUS_FAIL){
                            finalStatus = EnumConstant.MSG_DETAIL_SEND_STATUS_FAIL;
                        }
                    }
                    //如果除了自身还有未发送的，不去更新SendMessage状态
                    if(c <= 1){
                        sendMessage.setStatus(finalStatus);
                        sendMessageService.updateById(sendMessage);
                    }
                }else{
                    sendMessageService.updateById(sendMessage);
                }


            }
        }

        if(successSendDetail.size() > 0){
            sendMessageDetailService.updateMessageDetailStatus(EnumConstant.MSG_DETAIL_SEND_STATUS_SUC,successSendDetail);
            for (ReceiveMessage receive : receiveMessageList) {
                if(successSendDetail.contains(receive.getSendDetailId())){
                    receiveMessageService.save(receive);
                }
            }
        }
        if(failedSendDetail.size() > 0){
            sendMessageDetailService.updateMessageDetailStatus(EnumConstant.MSG_DETAIL_SEND_STATUS_FAIL,failedSendDetail);
        }
        log.info("消息发送完成！");
        result.mailMessageMap = mailMessageMap;
        result.successSendDetail = successSendDetail;
        result.failedSendDetail = failedSendDetail;
        return result;
    }
    /**
     * 通过消息模板发送消息
     * @param templateId 消息模板id
     * @param par        模板中替换的参数
     * @param receivers  额外的接收人
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendMessageByTemplate(int templateId, Map<String, Object> par, List<MessageReceiver> receivers) {
        MessageTemplate template = null;
        if(templateId > 0){
            template = messageTemplateService.getById(templateId);
        }
        if(template == null){
            throw new RuntimeException("发送消息时找不到消息模板[id="+templateId+"]");
        }
        sendMessageByTemplate(template,par,receivers);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendMessageByTemplate(String msgCode, Map<String, Object> par, List<MessageReceiver> receivers) {
        MessageTemplate template = null;
        if(StringTool.isNotEmpty(msgCode)){
            template = messageTemplateService.getTemplateByCode(msgCode);
        }
        if(template == null){
            throw new RuntimeException("发送消息时找不到消息模板[code="+msgCode+"]");
        }
        sendMessageByTemplate(template,par,receivers);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendMessageByTemplate(MessageTemplate template, Map<String, Object> par, List<MessageReceiver> receivers) {
        if(par == null){
            par = new HashMap<>(0);
        }
        //得到消息模版的接收人
        List<MessageTemplateReceiver> templateReceivers = templateReceiverService.getTemplateReceivers(template.getId());

        SendMessage message = new SendMessage();
        String title = StringTool.safeToString(template.getTitle(),"").trim();
        String content ;
        if(title.length() > 0){
            title = StringTool.replaceVariables(title,par);
        }
        if(title.length() > 100){
            title = title.substring(0,100);
        }
        int sendMode = NumberUtil.safeToInteger(template.getSendMode(),EnumConstant.MSG_TYPE_NORMAL);
        if(sendMode == EnumConstant.MSG_TYPE_NORMAL){
            //内部消息
            content = StringTool.safeToString(template.getContent(),"").trim();
        }else if(sendMode == EnumConstant.MSG_TYPE_EMAIL){
            //邮件
            content = StringTool.safeToString(template.getMailContent(),"").trim();
        }else{
            content = StringTool.safeToString(template.getContent(),"").trim();
        }
        if(content.length() > 0){
            content = StringTool.replaceVariables(content,par);
        }
        message.setTitle(title);
        message.setContent(content);
        message.setType(sendMode);
        message.setFromUserId(0);
        message.setTimer(0);
        message.setLevel(template.getLevel());

        for (MessageTemplateReceiver messageReceiver : templateReceivers) {
            message.addReceiver(messageReceiver);
        }
        //新增的接收人
        if(receivers != null && receivers.size() > 0){
            message.addReceiver(receivers);
        }
        if(message.getReceiver() == null || message.getReceiver().size() == 0){
            log.warn("消息模板["+template.getCode()+"]发送消息时没有接收人！");
            return;
        }
        //插入消息
        this.addMessage(message);
        if(template.getLevel() == 1){
            //消息重要度为"紧急"
            MessageSendModel qd = new MessageSendModel();
            qd.setSendId(message.getId());
            this.sendSynMessage(qd);
        }

    }


    @Override
    public int getReceiveCount(Map<String, Object> map) {
        return messageMapper.getReceiveCount(map);
    }

    private void readUser(List<OrgTree> treeList,Set<Integer> userId,Set<Integer> allOrgId){
        for(OrgTree tree : treeList){
            allOrgId.add(tree.getOrgId());
            if(EnumConstant.ORG_TYPE_USER.equals(tree.getOrgType())){
                userId.add(tree.getUserId());
            }
            List<OrgTree> list = tree.getChilds();
            if(list != null && list.size() > 0){
                readUser(list,userId,allOrgId);
            }
        }
    }


}
